package com.enerccio.commonJ.system;

import java.util.HashMap;
import java.util.Map;

import com.enerccio.commonJ.objects.Condition;
import com.enerccio.commonJ.objects.Condition.ConditionException;
import com.enerccio.commonJ.objects.commonJPackage;
import com.enerccio.commonJ.tools.SpecialVariable;

/**
 * Simple stacked compiled lisp virtual machine class.
 * @author enerccio
 *
 */
public class VM {
	
	/**
	 * Every thread can possibly have a VM created for it and they are stored here.
	 */
	private static SpecialVariable<VM> threadLocalVMs 
			= new SpecialVariable<VM>(new VM());
	
	/**
	 * Returns the VM that is currently bound to this thread.
	 * @return
	 */
	public static synchronized VM getVM(){
		return threadLocalVMs.get();
	}
	
	/**
	 * Creates new VM for the thread.
	 * @return
	 * @throws ConditionException if the VM is already present...
	 */
	public static synchronized VM newVM() throws ConditionException{
		if (threadLocalVMs.get() != null)
			new Condition("Only one VM per thread allowed.");
		threadLocalVMs.set(new VM());
		return threadLocalVMs.get();
	}
	
	private static Map<String, commonJPackage> loadedPackages = null;
	private static commonJPackage keywordsPackage;
	
	private VM(){	
		initBasePackages();
	}

	private synchronized void initBasePackages() {
		if (loadedPackages != null)
			return;
		loadedPackages = new HashMap<String, commonJPackage>();
		keywordsPackage = new commonJPackage("KEYWORDS");
		
		commonJPackage commonLisp = new commonJPackage("COMMON-LISP");
		commonJPackage commonJLisp = new commonJPackage("COMMON-J");
		commonLisp.addUsedPackage(commonJLisp);
		
		registerPackage(commonLisp, "COMMON-LISP", "CL", "COMMON-LISP-BASE");
		registerPackage(commonJLisp, "COMMON-J", "CJ");
	}
	
	/**
	 * Registers package to be used by all VMs
	 * @param p Package to be used
	 * @param name Base name for the package
	 * @param aliases Optional aliases for the package
	 */
	public synchronized void registerPackage(commonJPackage p, String name, String... aliases){
		loadedPackages.put(name, p);
		for (int i=0; i<aliases.length; i++)
			loadedPackages.put(aliases[i], p);
	}
	
	/**
	 * Returns the keyword package
	 * @return
	 */
	public commonJPackage getKeywordsPackage(){
		return keywordsPackage;
	}

	/**
	 * Returns currently bound package that is stored in *package*
	 * @return
	 */
	public synchronized commonJPackage getCurrentPackage() {
		// TODO
		return null;
	}

}
